package com.yang.service.impl;


import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yang.domain.entity.SeckillVoucher;
import com.yang.domain.entity.VoucherOrder;
import com.yang.mapper.SeckillVoucherMapper;
import com.yang.mapper.VoucherOrderMapper;
import com.yang.service.IVoucherOrderService;
import com.yang.service.MessageService;
import com.yang.utils.RedisIdWorker;
import com.yang.utils.UserHolder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

import static com.yang.constants.RabbitMqConstants.MESSAGE_QUEUE;
import static com.yang.constants.RedisConstants.SECKILL_STOCK_KEY;
import static com.yang.enums.SeckillTypeEnum.*;


/**
 * <p>
 *  服务实现类，基于MQ来解决异步下问题
 * </p>
 *
 * @author yang
 * @since 2022-12-22
 */
@Slf4j
@Service
public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService, InitializingBean {

    @Resource
    private RedisIdWorker redisIdWorker;
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private SeckillVoucherMapper seckillVoucherMapper;
    @Resource
    private MessageService messageService;

    /**
     * 做标记，判断该商品是否被处理过了
     */
    private final Map<Long, Boolean> localOverMap = new ConcurrentHashMap<>();

    /**
     * lua脚本
     */
    private static final DefaultRedisScript<Long> SECKILL_SCRIPT;

    static {
        SECKILL_SCRIPT = new DefaultRedisScript<>();
        SECKILL_SCRIPT.setLocation(new ClassPathResource("seckill.lua"));
        SECKILL_SCRIPT.setResultType(Long.class);
    }

    /**
     * 判断购买资格
     * @param voucherId 拿到抢到的优惠券id
     * @return 该用户是否有秒杀的资格
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long seckillVoucher(Long voucherId) {
        // 秒杀内存标记
        if (localOverMap.get(voucherId)){
            return SECKILL_ERROR.getCode();
        }
        Long userId = 2L;

        long orderId = redisIdWorker.nextId("order");

        Long result = stringRedisTemplate.execute(
                SECKILL_SCRIPT,
                //传递key参数集合，没有key，传递一个空集合
                Collections.emptyList(),
                voucherId.toString(), userId.toString(), String.valueOf(orderId)
        );

        if (result == null){
            return SECKILL_ERROR.getCode();
        }

        if (STOCK_NULL.getCode().equals(result)){
            localOverMap.put(voucherId, true);
            return STOCK_NULL.getCode();
        }

        if (REPEAT_ORDER.getCode().equals(result)){
            return REPEAT_ORDER.getCode();
        }

        boolean success = reduceStock(voucherId);

        if (!success){
            return SECKILL_ERROR.getCode();
        }

        VoucherOrder voucherOrder = new VoucherOrder();
        voucherOrder.setVoucherId(voucherId);
        voucherOrder.setUserId(userId);
        voucherOrder.setId(orderId);

        // todo 将消息预发送和扣减库存操作绑定成一个事务

        // todo 通过定时器将预发送和发送中一并发送出去

        // 将消息保存到DB，预发送消息
        Long messageId = messageService.addWaitSendMessage(MESSAGE_QUEUE, JSONUtil.toJsonStr(voucherOrder));


        if (messageId == null) {
            // 真正扣减库存
        }

        // 发送消息，将消息状态改为已发送
        messageService.confirmAndSendMessage(messageId);

        return orderId;
    }

    private boolean reduceStock(Long voucherId) {
        int numAttempts = 0;
        do {
            numAttempts++;
            boolean success = seckillVoucherMapper.updateStock(voucherId);
            if (success) {
                return true;
            }
        }while (numAttempts < 5);
        return false;
    }

    /**
     * 系统初始化,将商品信息加载到redis和本地内存
     */
    @Override
    public void afterPropertiesSet()  {
        List<SeckillVoucher> voucherList = seckillVoucherMapper.selectList(null);

        if (voucherList == null) {
            return;
        }

        for (SeckillVoucher voucher : voucherList) {
            String key = SECKILL_STOCK_KEY + voucher.getVoucherId();
            stringRedisTemplate.opsForValue().set(key,voucher.getStock().toString());
            //初始化商品都是没有处理过的
            localOverMap.put(voucher.getVoucherId(), false);
        }
    }

}
